package com.zijidelu.luframework.plugins.tomlock.redisson;

import com.zijidelu.luframework.plugins.tomlock.core.config.LockProperties;
import com.zijidelu.luframework.plugins.tomlock.core.data.BaseTomLockData;
import com.zijidelu.luframework.plugins.tomlock.core.data.TomLockResult;
import com.zijidelu.luframework.plugins.tomlock.core.exception.TomLockException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;

/**
 * @author ZIJIDELU
 * @datetime 2025/4/13 21:42
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RedissonLockExecutorImpl implements RedissonLockExecutor {
    private final RedissonClient redissonClient;
    private final LockProperties lockProperties;

    @Override
    public void calcLockRun(TomLockResult tomLockResult, Runnable runnable) {
        calcLockRun(tomLockResult, () -> {
            runnable.run();
            return null;
        });
    }

    @Override
    public <T> T calcLockRun(TomLockResult tomLockResult, Supplier<T> supplier) {
        BaseTomLockData tomLockData = new BaseTomLockData();
        tomLockData.setLockPrefix(lockProperties.getLockPrefix());
        tomLockData.setLockDesc("TomLock");
        tomLockData.setExpire(lockProperties.getExpire());
        tomLockData.setTimeout(lockProperties.getTimeout());
        return calcLockRun(tomLockData, tomLockResult, supplier);
    }

    @Override
    public <T> T calcLockRun(BaseTomLockData tomLockData, TomLockResult tomLockResult, Supplier<T> supplier) {
        return run(tomLockData, calcLock(tomLockData, tomLockResult), supplier);
    }

    @Override
    public RLock calcLock(BaseTomLockData tomLockData, TomLockResult tomLockResult) {
        final List<RLock> rLockList = new ArrayList<>();
        for (TomLockResult.LockResult lockResult : tomLockResult.getResultList()) {
            final RLock lock = redissonClient.getLock(lockResult.toRedisKey(tomLockData.getLockPrefix()));
            rLockList.add(lock);
        }

        return rLockList.size() == 1
                ? rLockList.get(0)
                : redissonClient.getMultiLock(rLockList.toArray(RLock[]::new));
    }

    @Override
    public <T> T run(BaseTomLockData tomLockData, RLock rLock, Supplier<T> supplier) {
        final String lockName = tomLockData.getLockDesc();

        try {

            try {
                log.info("==> 尝试获取锁：{}", lockName);
                if (!rLock.tryLock(tomLockData.getTimeout(), tomLockData.getExpire(), BaseTomLockData.TIME_UNIT)) {
                    log.error("获取锁失败：{}", lockName);

                    throw new TomLockException("获取锁失败：" + lockName);
                }


                log.info("获取锁成功：{}", lockName);

                return supplier.get();

            } finally {
                if (Objects.nonNull(rLock)) {
                    if (rLock instanceof RedissonMultiLock) {
                        rLock.unlock();
                    } else {
                        final boolean isHeldByCurrentThread = rLock.isHeldByCurrentThread();
                        log.info("当前线程是否持有锁：{}", isHeldByCurrentThread);

                        if (isHeldByCurrentThread) {
                            final boolean isLocked = rLock.isLocked();
                            log.info("当前线程是否锁定：{}", isLocked);

                            if (isLocked) {
                                log.info("<== 释放锁：{}", lockName);
                                rLock.unlock();
                            }
                        }

                    }
                }
            }

        } catch (Exception e) {
            log.error("分布式锁内执行异常: {}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }


}
